﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;

using dotUML.Core.Common;

namespace dotUML.Core.Parser
{
    /// <summary>
    /// Base abstract class for parsing xml.
    /// </summary>
    abstract class XmlParserBase : IXmlParser
    {

        #region IXmlParser Members

        protected List<IError> _errors;

        /// <summary>
        /// List of parsing errors.
        /// </summary>
        public List<dotUML.Core.Common.IError> Errors
        {
            get
            {
                return _errors;
            }
            set
            {
                _errors = value;
            }
        }

        /// <summary>
        /// Parse an xml-file and create a dotUML model based on the file's content.
        /// </summary>
        /// <param name="Xml">Xml diagram description.</param>
        /// <param name="Model">dotUML model.</param>
        /// <returns>True if parsing is succsessful, otherwise false.</returns>
        public virtual bool Parse(string Xml, dotUML.Core.Common.IModel Model)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Validate an xml-file against an xml-schema.
        /// </summary>
        /// <param name="Xml">String representation of an xml-file.</param>
        /// <returns>True if the file is valid, otherwise - false.</returns>
        public virtual bool ValidateXml(string Xml)
        {
            throw new NotImplementedException();
        }

        #endregion

        protected IModel _model;
        /// <summary>
        /// dotUML model.
        /// </summary>
        public IModel Model
        {
            get
            {
                return _model;
            }
            set
            {
                _model = value;
            }
        }

        /// <summary>
        /// Parameterless class constructor.
        /// </summary>
        public XmlParserBase()
        {
            this.Errors = new List<IError>();
        }

        /// <summary>
        /// Class constructor.
        /// </summary>
        /// <param name="Errors">Parsing errors list.</param>
        public XmlParserBase(List<IError> Errors)
        {
            this.Errors = Errors;
        }

        /// <summary>
        /// Create new parsing error.
        /// </summary>
        /// <param name="ErrorType">Error type.</param>
        /// <param name="Message">Error message.</param>
        protected virtual void NewError(EErrorType ErrorType, string Message)
        {
            Errors.Add(ErrorFactory.NewError(ErrorType, Message));
        }

        /// <summary>
        /// Parse an element reference.
        /// </summary>
        /// <param name="Xml">Xml containing an element reference.</param>
        protected virtual void ParseRef(string Xml)
        {
        }

        /// <summary>
        /// Check whether an attribute exists in an xml element.
        /// </summary>
        /// <param name="AttributeName">Attribute name.</param>
        /// <param name="Element">Xml element.</param>
        /// <returns>True if the attribute exists, otherwise - false.</returns>
        protected virtual bool AttributeExists(string AttributeName, XElement Element)
        {
            if (Element == null)
            {
                throw new ParserException(Resources.XmlParserBase_AttributeExists_NullReference);
            }

            return (Element.Attribute(AttributeName) != null);
        }

        /// <summary>
        /// Get the value of a specific string attribute of an xml element.
        /// </summary>
        /// <param name="AttributeName">Attribute name.</param>
        /// <param name="Xml">Xml element.</param>
        /// <param name="IsOptionalAttribute">Determines if the attribute can be optional.</param>
        /// <returns>Attribute value if the attribute exists. 
        /// Empty string if the attribute is optional and does not exist. 
        /// Throws exception if the attribute is required and does not exist.</returns>
        protected virtual string ParseStringAttribute(string AttributeName, XElement Xml, bool IsOptionalAttribute)
        {
            string result = string.Empty;
            if (AttributeExists(AttributeName, Xml))
            {
                result = Xml.Attribute(AttributeName).Value;
            }
            else
            {
                if (!IsOptionalAttribute)
                {
                    throw new ParserException(string.Format(Resources.XmlParserBase_ParseAttribute_MissingAttribute, AttributeName));
                }
            }
            return result;
        }

        /// <summary>
        /// Get the value of a specific string attribute of an xml element.
        /// Attribute is required.
        /// </summary>
        /// <param name="AttributeName">Attribute name.</param>
        /// <param name="Xml">Xml element.</param>
        /// <returns>Attribute value if the attribute exists, otherwise throws exception.</returns>
        protected virtual string ParseStringAttribute(string AttributeName, XElement Xml)
        {
            return ParseStringAttribute(AttributeName, Xml, false);
        }

        /// <summary>
        /// Get the value of a specific enum attribute of an xml element.
        /// </summary>
        /// <param name="AttributeName">Attribute name.</param>
        /// <param name="Xml">Xml element.</param>
        /// <param name="EnumType">Type of expected enumeration.</param>
        /// <param name="IsOptionalAttribute">Determines if the attribute can be optional.</param>
        /// <returns>Attribute value if the attribute exists.
        /// Null if the attribute is optional and does not exist. 
        /// Throws exception if the attribute is required and does not exist.</returns>
        protected virtual object ParseEnumAttribute(string AttributeName, XElement Xml, Type EnumType, bool IsOptionalAttribute)
        {
            object result = null;
            if (AttributeExists(AttributeName, Xml))
            {
                result = Enum.Parse(EnumType, Xml.Attribute(AttributeName).Value, true);
            }
            else
            {
                if (!IsOptionalAttribute)
                {
                    throw new ParserException(string.Format(Resources.XmlParserBase_ParseAttribute_MissingAttribute, AttributeName));
                }
            }
            return result;
        }

        /// <summary>
        /// Get the value of a specific enum attribute of an xml element.
        /// Attribute is required.
        /// </summary>
        /// <param name="AttributeName">Attribute name.</param>
        /// <param name="Xml">Xml element.</param>
        /// <param name="EnumType">Type of expected enumeration.</param>
        /// <returns>Attribute value if the attribute exists, otherwise throws exception.</returns>
        protected virtual object ParseEnumAttribute(string AttributeName, XElement Xml, Type EnumType)
        {
            return ParseEnumAttribute(AttributeName, Xml, EnumType, false);
        }

        /// <summary>
        /// Get the value of a stereotypes attribute. Attribute is optional.
        /// </summary>
        /// <param name="AttributeName">Attribute name.</param>
        /// <param name="Xml">Xml element.</param>
        /// <returns>List of stereotypes via IStereotype interface if the attribute exists,
        /// otherwise - empty list of stereotypes.</returns>
        protected virtual List<IStereotype> ParseStereotypeAttribute(string AttributeName, XElement Xml)
        {
            List<IStereotype> result = new List<IStereotype>();
            if (AttributeExists(AttributeName, Xml))
            {
                string[] stereotypes = Xml.Attribute(AttributeName).Value.Split(',');
                foreach(string stereotype in stereotypes)
                {
                    IStereotype stereotypeObj = Model.GetStereotype(stereotype);
                    if (stereotypeObj != null)
                    {
                        result.Add(stereotypeObj);
                    }
                    else
                    {
                        throw new ParserException(string.Format(Resources.XmlParserBase_ParseStereotypeAttribute_MissingStereotype, stereotype));
                    }
                }
            }
            return result;
        }

    }
}
